package com.google.ant.extensions.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import java.util.StringTokenizer;

import org.apache.tools.ant.util.FileUtils;

public final class IOUtils {
    private static final int RETRY_SLEEP_MILLIS = 10;
    private static final String SEPARATOR = System.getProperty("line.separator");

    private IOUtils() {
    }

    public static void appendLine(final String line, final File file) {
        appendLines(Arrays.asList(new String[]{line}), file);
    }

    public static void appendLines(Collection<String> lines, final File file) {
        Collection<String> existed = getLines(file);
        Collection<String> quolified = new ArrayList<String>();
        for (String line : lines) {
            if (!existed.contains(line) && !quolified.contains(line)) {
                quolified.add(line);
            }
        }

        StringBuffer sb = new StringBuffer();
        for (String line : quolified) {
            sb.append(line);
            sb.append(SEPARATOR);
        }
        Writer out = null;
        try {
            String filename = file.getAbsolutePath();
            out = new FileWriter(filename, true);
            out.write(sb.toString(), 0, sb.toString().length());
        } catch (IOException e) {
            System.err.println("Failed to append lines " + sb.toString() + " to file : " + file);
        } finally {
            FileUtils.close(out);
        }
    }

    public static Collection<String> getLines(final File file) {
        BufferedReader reader = null;
        String value = null;
        Collection<String> lines = new ArrayList<String>();
        try {
            reader = new BufferedReader(new FileReader(file));
            value = FileUtils.readFully(reader);
        } catch (IOException ex) {
            return lines;
        } finally {
            FileUtils.close(reader);
        }
        StringTokenizer stoken = new StringTokenizer(value, SEPARATOR);
        while (stoken.hasMoreTokens()) {
            lines.add(stoken.nextToken());
        }
        return lines;
    }

    public static void mkDir(File dir) {
        if (dir == null) {
            throw new RuntimeException("dir attribute is required");
        }

        if (dir.isFile()) {
            throw new RuntimeException("Unable to create directory as a file "
                                    + "already exists with that name: " + dir.getAbsolutePath());
        }

        if (!dir.exists()) {
            boolean result = doMkDirs(dir);
            if (!result) {
                String msg = "Directory " + dir.getAbsolutePath()
                             + " creation was not successful for an unknown reason";
                throw new RuntimeException(msg);
            }
        }
    }

    /**
     * Attempt to fix possible race condition when creating directories on
     * WinXP, also Windows2000. If the mkdirs does not work, wait a little and
     * try again.
     */
    private static boolean doMkDirs(File f) {
        if (!f.mkdirs()) {
            try {
                Thread.sleep(RETRY_SLEEP_MILLIS);
                return f.mkdirs();
            } catch (InterruptedException ex) {
                return f.mkdirs();
            }
        }
        return true;
    }

    public static void removeDir(File d) {
        String[] list = d.list();
        if (list == null) {
            list = new String[0];
        }
        for (int i = 0; i < list.length; i++) {
            String s = list[i];
            File f = new File(d, s);
            if (f.isDirectory()) {
                removeDir(f);
            } else {
                delete(f);
            }
        }
        delete(d);
    }

    public static void delete(File f) {
        if (!f.delete()) {
            if (isWindows()) {
                System.gc();
            }
            try {
                Thread.sleep(RETRY_SLEEP_MILLIS);
            } catch (InterruptedException ex) {
                // Ignore Exception
            }
            if (!f.delete()) {
                f.deleteOnExit();
            }
        }
    }

    private static boolean isWindows() {
        String osName = System.getProperty("os.name").toLowerCase(Locale.US);
        return osName.indexOf("windows") > -1;
    }
}